package server;

import java.awt.event.ActionListener;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import main.H;

/**
 * Find a server on a LAN.
 * 
 * Usage:
    FindServer fs = new FindServer(baseIpAdd, port, new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            if(fs.isDone()) {
                InetAddress inetAdd = fs.getServerFound();
                if(inetAdd != null) {
                    txtHost.setText(inetAdd.getHostAddress().toString());
                    JOptionPane.showMessageDialog(null, "Found server @ " + inetAdd.toString());
                }
                else {
                    JOptionPane.showMessageDialog(null, "Couldn't find any server!");
                }
            }
        }
    });
    if(!fs.isValid()) {
        JOptionPane.showMessageDialog(null, "Please enter a valid IP v4 address...");
        return;
    }
    Thread t = new Thread(fs);
    t.start();
 * 
 * @author benjamin
 */
public class FindServer implements Runnable {

    private boolean isValid = false;
    private boolean isDone = false;
    private String baseIpAdd = "192.168.1";
    private int port = 4444;
    private InetAddress serverFound = null;
    private ActionListener listener = null;
    private javax.swing.Timer timer = null;

    /**
     * Find a server on the LAN
     * @param ipAdd IP v4 address
     * @param _port Port between 1024 - 65535.
     */
    public FindServer(String ipAdd, int _port, ActionListener _listener) {
        baseIpAdd = makeBaseIp(ipAdd);
        port = _port;
        listener = _listener;
    }

    /*
     * @return Returns the IP Address of the found server. If no server is found, it
     * returns null.
     */
    public InetAddress getServerFound() {
        return serverFound;
    }

    /**
     * @return Returns TRUE if the specified IP in the constructor is valid.
     */
    public boolean isValid() {
        return isValid;
    }

    /**
     * 
     * @return Returns TRUE when done looping through the IP range 0-255
     */
    public boolean isDone() {
        if(isDone)
            timer.stop();
        return isDone;
    }

    @Override
    public void run() {
        isDone = false;
        timer = new javax.swing.Timer(1000, listener);
        timer.start();
        //H.PL("Trying to find a server...");
        int i = 0;
        while(i <= 255) {
            InetAddress inetAdd = createIpAddressFromBase(i);
            if(inetAdd == null) {
                //H.PL("error!");
                return;
            }
            Boolean isAlive = isAlive(inetAdd, this.port, 10);
            H.PL(inetAdd.getHostAddress() + " is server? " + isAlive);
            if (isAlive) {
                serverFound = inetAdd;
                break;
            }
            i++;
        }
        isDone = true;
    }

    /**
     * Check if a host(server) on a given IP address and port number is "alive".
     * @param host A valid IP v4 address.
     * @param port A valid port number between 1024 - 65535.
     * @param timeout The connection timeout in milliseconds (ms). The lower the 
     * timeout the lower probability of finding a host if the connection is slow
     * but faster search. (Optimal timeout is between 10-100 ms).
     * @return TRUE if the host(server) is 'alive'.
     */
    public static boolean isAlive(InetAddress host, int port, int timeout) {
        try {
            Socket socket=new Socket();
            socket.connect(new InetSocketAddress(host,port),timeout);
            boolean isConnected = socket.isConnected();
            if(isConnected) {
                //Close the connection.
                socket.close();
            }
            return isConnected;
        } catch (Exception ex) {
            return false;
        }
    }

    /**
     * Create a IP v4 address from a sub value. E.g. 192.168.0 with input 0 will
     * return 192.168.0.0 - a value lower than 0 or higher than 255 will return 
     * null.
     * @param subDval A number between 0 - 255.
     * @return Returns a valid IP v4 address or null if input is 'incorrect'.
     */
    private InetAddress createIpAddressFromBase(int subDval) {
        try {
            return InetAddress.getByName(baseIpAdd + "." + subDval);
        } catch (UnknownHostException ex) {
            return null;
        }
    }

    /**
     * Make a base IP v4 address from an IP. E.g. 192.168.0.0 will become
     * 192.168.0
     * @param ipAdd A valid IP v4 address.
     * @return Returns a base IP.
     */
    private String makeBaseIp(String ipAdd) {
        String[] ipAddSplit = ipAdd.split("\\.");
        if(ipAddSplit.length < 3) {
            return "-1";
        }
        int subA = Integer.parseInt(ipAddSplit[0]);
        int subB = Integer.parseInt(ipAddSplit[1]);
        int subC = Integer.parseInt(ipAddSplit[2]);
        if( subA >= 0 && subA <= 255 &&
            subB >= 0 && subB <= 255 &&
            subC >= 0 && subC <= 255
        ) {
            isValid = true;
        }
        return subA + "." + subB + "." + subC;
    }
}
